package org.purl.net.sparql.geo;

import com.hp.hpl.jena.sparql.expr.ExprEvalException;
import com.hp.hpl.jena.sparql.expr.NodeValue;
import com.hp.hpl.jena.sparql.function.FunctionBase2;
import uk.me.jstott.jcoord.*;

/**
 * SPARQL Extension function that calculates the distance 
 * between two points.
 * 
 * The function accepts two literal values, as defined by a 
 * GeoRSS point. i.e. a latitude and longitude expressed 
 * as a decimal and separated by a single space.
 * 
 * @author ldodds
 */
public class DistanceGeoRSS extends FunctionBase2 
{
	public NodeValue exec(NodeValue firstPoint, NodeValue secondPoint) 
	{
		checkNodeType(firstPoint);
		checkNodeType(secondPoint);
		
		String one = firstPoint.asString();
		checkFormat(one);
		String[] coords = one.split(" ");
		if (coords.length > 2) {
			throw new ExprEvalException("Expecting only latitude and longitude");
		}
		LatLng a = new LatLng(Double.parseDouble(coords[0]), 
				Double.parseDouble(coords[1]));

		String two = secondPoint.asString();
		checkFormat(two);
		String[] coords2 = two.split(" ");
		if (coords2.length > 2) {
			throw new ExprEvalException("Expecting only latitude and longitude");
		}		
		LatLng b = new LatLng(Double.parseDouble(coords2[0]), 
				Double.parseDouble(coords2[1]));

		double distance = a.distance(b);
		return NodeValue.makeDouble(distance);
	}

	
	private void checkNodeType(NodeValue value) {
		if (value.asNode().isURI() || 
			value.asNode().isBlank() ) {
			throw new ExprEvalException("Cannot calculate distance from a resource");
		}
	}
	
	private void checkFormat(String value) {
		if ( value.indexOf(" ") == -1 ) {
			throw new ExprEvalException("GeoRSS points are separated by spaces");
		}		
	}
}
